function gewMeshManager(game)
{
  this.game = game;
  this.gl = game.gl;
  this.meshList = {};
}
gewMeshManager.prototype =
{
  removeMesh:function(name)
  {
    var mesh = this.meshList[name];
    if(!mesh)
      return;
    var gl = this.gl;
    if(mesh.isCreateVBO==true)
    {
      var type = mesh.meshType;
      var glBuffers = mesh.glBuffers;
      if(type == GEW_STATIC_MESH)
      {
        for(var buf in glBuffers)
        {
          gl.deleteBuffer(glBuffers.buf);
        }
      }
      else if(type == GEW_KEYFRAME_MESH )
      {
        for(var buf in glBuffers)
        {
          var len = glBuffers.buf.length;
          for(var i=0;i<len;i++)
            gl.deleteBuffer(glBuffers.buf[i]);
        }
      }
    }
    delete this.meshList[name];    
  },
  createObjRequest:function(name,url,isCreateVBO,queryItem) // default: auto create VBO 
  {    
    var xhr = new XMLHttpRequest();
    var self = this;
    var mesh = new gewStaticMesh();
    mesh.renderType = GEW_TRIANGLES_NON_INDICES;
    mesh.name = name;
    mesh.isCreateVBO = isCreateVBO;
    mesh.gl = this.gl;
    mesh.url = url;
    this.meshList[name] = mesh;
    xhr.onreadystatechange = function() 
    {
      if(xhr.readyState == 4) 
      {
        if (xhr.status == 200 || xhr.status == 0) 
        {
          queryItem.status = GEW_DOWNLOAD_STATUS_FINISHED;
          mesh.parseObj(xhr.responseText);
        } 
        else 
        {
          alert("gewMeshManager::loadObj: Error when load "+url+"!");  
        }
      }
    }
    xhr.open("GET", url);
    xhr.overrideMimeType("text/plain; charset=x-user-defined");
    xhr.setRequestHeader("Content-Type", "text/plain");
    //xhr.send();
    return xhr;
  },
  createISM1Request:function(name,url,isCreateVBO,queryItem)//default : auto create VBO
  {
    var xhr = new XMLHttpRequest();
    var self = this;
    var mesh = new gewStaticMesh();
    mesh.isCreateVBO = isCreateVBO;
    mesh.name = name;
    mesh.gl = this.gl;
    mesh.url = url;
    this.meshList[name] = mesh;
    xhr.onreadystatechange = function() 
    {
      if(xhr.readyState == 4) 
      {
        if (xhr.status == 200 || xhr.status == 0) 
        {
          queryItem.status = GEW_DOWNLOAD_STATUS_FINISHED;
          mesh.parseISM1(xhr.responseText);
        } 
        else 
        {
          alert("gewMeshManager::loadISM1: Error when load "+url+"!");  
        }
      }
    }
    xhr.open("GET", url, true);
    xhr.overrideMimeType("text/plain; charset=x-user-defined");
    xhr.setRequestHeader("Content-Type", "text/plain");
    //xhr.send(null);
    return xhr;
  },
  createSAM1Request:function(name,url,isCreateVBO,queryItem)//default : auto create VBO
  {
    var xhr = new XMLHttpRequest();
    var self = this;
    var mesh = new gewSkinnedMesh();
    mesh.isCreateVBO = isCreateVBO;
    mesh.name = name;
    mesh.gl = this.gl;
    mesh.url = url;
    this.meshList[name] = mesh;
    xhr.onreadystatechange = function() 
    {
      if(xhr.readyState == 4) 
      {
        if (xhr.status == 200 || xhr.status == 0) 
        {
          queryItem.status = GEW_DOWNLOAD_STATUS_FINISHED;
          mesh.parseSAM1(xhr.responseText);
        } 
        else 
        {
          alert("gewMeshManager::loadSAM1: Error when load "+url+"!");  
        }
      }
    }
    xhr.open("GET", url, true);
    xhr.overrideMimeType("text/plain; charset=x-user-defined");
    xhr.setRequestHeader("Content-Type", "text/plain");
    //xhr.send(null);
    return xhr;
  },
  creatKAM1Request:function(name,url,isCreateVBO,queryItem)//default : auto create VBO
  {
    var xhr = new XMLHttpRequest();
    var self = this;
    var mesh = new gewKeyframeMesh();
    mesh.gl = this.gl;
    mesh.name = name;
    mesh.url = url;
    this.meshList[name] = mesh;
    mesh.isCreateVBO = isCreateVBO;
    xhr.onreadystatechange = function() 
    {
      if(xhr.readyState == 4) 
      {
        if (xhr.status == 200 || xhr.status == 0) 
        {
          queryItem.status = GEW_DOWNLOAD_STATUS_FINISHED;
          mesh.parseKAM1(xhr.responseText);
        } 
        else 
        {
          alert("gewMeshManager::loadKAM1: Error when load "+url+"!");  
        }
      }
    }
    xhr.open("GET", url, true);
    xhr.overrideMimeType("text/plain; charset=x-user-defined");
    xhr.setRequestHeader("Content-Type", "text/plain");
    //xhr.send(null);
    return xhr;
  },
  getMesh:function(name)
  {
    return this.meshList[name];
  }
}

function gewStaticMesh()
{
  this.meshType = GEW_STATIC_MESH;
  this.renderType; // specify how to vertices data is stored in this mesh
  this.name = null;
  this.isReady = false;
  this.vertices = null;
  this.texcoords = null;
  this.normals = null;
  this.indices = null;
  this.lodNumber = 0;
  this.glBuffers = {}; 
  this.aabb = new gewAabb();
  this.boundingSphere = new gewSphere();
  this.gl = null;
  this.isCreateVBO = true;
  this.isReadyForBatch = false;
  this.maxObjPerBatch = 0;
  this.batchGLBuffers = {}; // the glbuffer used by Vertex-Constant Batching
  this.url = null;
}
gewStaticMesh.prototype =
{
  parseISM1:function(data)
  {
    if( data[0]!='i'||data[1]!='s'||data[2]!='m'||data[3]!='1')
    {
      alert("gewStaticMesh::parseISM1:This file isn't a ism1 file");
    }
    this.renderType = GEW_TRIANGLES_INDICES;
    var offset = 4;
    var type = gewReadUByte(data, offset);
    offset++;
    var ver_fp_format = gewReadUByte(data, offset);offset++;
    var vMin = gewReadInt16(data,offset);offset+=2;
    var vMax = gewReadInt16(data,offset);offset+=2; 
    var tex_fp_format = gewReadUByte(data, offset);offset++;
    var tMin = gewReadInt16(data,offset);offset+=2;
    var tMax = gewReadInt16(data,offset);offset+=2; 
    var nor_fp_format = gewReadUByte(data, offset);offset++;
    var nMin = gewReadInt16(data,offset);offset+=2;
    var nMax = gewReadInt16(data,offset);offset+=2;     
    var vertex_count = gewReadUInt32(data, offset);
    offset += 4;
    var lod_count = gewReadUByte(data, offset);
    offset++;
    this.lodNumber = lod_count;
    var indies_count = [];
    this.indices = [];
    for(var i=0;i<lod_count;i++)
    {
      indies_count[i] = gewReadUInt32(data, offset);
      offset += 4;
      this.indices[i] = new WebGLUnsignedShortArray(indies_count[i]);
    }
    offset = 64;var vertices = new WebGLFloatArray(vertex_count*3);
    var texcoords = new WebGLFloatArray(vertex_count * 2);
    var normals = new WebGLFloatArray(vertex_count * 3);
    this.vertices = vertices;
    this.texcoords = texcoords;
    this.normals = normals;
    var byte_per_ver_coord = gewFixedPointSize(ver_fp_format);
    var byte_per_tex_coord = gewFixedPointSize(tex_fp_format);
    var byte_per_nor_coord = gewFixedPointSize(nor_fp_format);
    for (i = 0; i < vertex_count; i++) 
    {
      vertices[i * 3] = gewReadFixed(data, offset,ver_fp_format,vMin,vMax);
      offset += byte_per_ver_coord;
      vertices[i * 3 + 1] = gewReadFixed(data, offset,ver_fp_format,vMin,vMax);
      offset += byte_per_ver_coord;
      vertices[i * 3 + 2] = gewReadFixed(data, offset,ver_fp_format,vMin,vMax);
      offset += byte_per_ver_coord;
      texcoords[i * 2] = gewReadFixed(data, offset,tex_fp_format,tMin,tMax);
      offset += byte_per_tex_coord;
      texcoords[i * 2 + 1] = gewReadFixed(data, offset,tex_fp_format,tMin,tMax);
      offset += byte_per_tex_coord;
      normals[i * 3] = gewReadFixed(data, offset,nor_fp_format,nMin,nMax);
      offset += byte_per_nor_coord;
      normals[i * 3 + 1] = gewReadFixed(data, offset,nor_fp_format,nMin,nMax);
      offset += byte_per_nor_coord;
      normals[i * 3 + 2] = gewReadFixed(data, offset,nor_fp_format,nMin,nMax);
      offset += byte_per_nor_coord;
    }
    for (i = 0; i < lod_count; i++) 
    {
      var indices = this.indices[i];
      for (var j = 0; j < indies_count[i]; j++) 
      {
        indices[j] = gewReadUInt16(data, offset);
        offset += 2;
      }
    }
    var aabb = this.aabb;
    var sphere = this.boundingSphere;
    var aabbMin = new gewVec3();
    var aabbMax = new gewVec3();
    aabbMin.x = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
    offset += 4;
    aabbMin.y = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
    offset += 4;
    aabbMin.z = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
    offset += 4;
    aabbMax.x = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
    offset += 4;
    aabbMax.y = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
    offset += 4;
    aabbMax.z = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
    aabb.init(aabbMin,aabbMax);
    
    offset += 4;
    sphere.center.x = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
    offset += 4;
    sphere.center.y = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
    offset += 4;
    sphere.center.z = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
    offset += 4;
    sphere.radius = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
    offset += 4;
    
    if(this.isCreateVBO!=false)
      this.makeVBO(this.gl)
    this.isReady = true;
  },
  // Base on the .obj file parse of Ilmari Heikkinen : http://fhtr.blogspot.com/2009/12/3d-models-and-parsing-binary-data-with.html
  parseObj:function(data)
  {
    this.lodNumber = 1;
    var geo_faces = [],
        nor_faces = [],
        tex_faces = [],
        raw_vertices = [],
        raw_normals = [],
        raw_texcoords = [];
    var lines = data.split("\n");
    var hashChar = '#'.charCodeAt(0);
    for (var i=0; i<lines.length; i++) {
      var l = lines[i];
      var vals = l.replace(/^\s+|\s+$/g, '').split(" ");
      if (vals.length == 0) continue;
      if (vals[0].charCodeAt(0) == hashChar) continue;
      switch (vals[0]) {
        case "g": // named object mesh [group]?
          break;
        case "v":
          raw_vertices.push(parseFloat(vals[1]));
          raw_vertices.push(parseFloat(vals[2]));
          raw_vertices.push(parseFloat(vals[3]));
          break;
        case "vn":
          raw_normals.push(parseFloat(vals[1]));
          raw_normals.push(parseFloat(vals[2]));
          raw_normals.push(parseFloat(vals[3]));
          break;
        case "vt":
          raw_texcoords.push(parseFloat(vals[1]));
          raw_texcoords.push(parseFloat(vals[2]));
          break;
        case "f":
          // triangulate the face as triangle fan
          var faces = [];
          for (var j=1, v; j<vals.length; j++) {
            if (j > 3) {
              faces.push(faces[0]);
              faces.push(v);
            }
            v = vals[j];
            faces.push(v);
          }
          for (var j=0; j<faces.length; j++) {
            var f = faces[j];
            var a = f.split("/");
            geo_faces.push(parseInt(a[0]) - 1);
            if (a.length > 1)
              tex_faces.push(parseInt(a[1]) - 1);
            if (a.length > 2)
              nor_faces.push(parseInt(a[2]) - 1);
          }
          break;
      }
    }
    this.vertices = this.lookup_faces(raw_vertices, geo_faces, 3);
    if (tex_faces.length > 0)
      this.texcoords = this.lookup_faces(raw_texcoords, tex_faces, 2);
    if (nor_faces.length > 0)
      this.normals = this.lookup_faces(raw_normals, nor_faces, 3);
     
    //Calculate bounding volume
    var bbox = this.aabb;
    for (var i=0; i<raw_vertices.length; i+=3) 
    {
      var x = raw_vertices[i],
      y = raw_vertices[i+1],
      z = raw_vertices[i+2];
      bbox.addInternalPointXYZ(x,y,z);    
    }
    bbox.init(bbox.min,bbox.max);
    this.boundingSphere.center = bbox.min.add(bbox.max).divScalar(2);
    this.boundingSphere.radius = this.boundingSphere.center.sub(bbox.min).getLength();
       
    if(this.isCreateVBO!=false)
      this.makeVBO(this.gl)  
    this.isReady = true;
  },
  // Private func use to parse .obj file
  lookup_faces : function(verts, faces, sz) 
  {
    var faceLen = faces.length;
    var v = new WebGLFloatArray(faceLen*sz);
    var buffOffset = 0;
    for (var i=0; i<faceLen; i++) 
    {
      var offset = faces[i] * sz;
      for (var j=0; j<sz; j++)
      {
        v[buffOffset] = verts[offset+j];
        buffOffset++;
      }
    }
    return v;
  },
  // Create GL buffer object that contain mesh data
  makeVBO : function(gl) 
  {
    this.isCreateVBO = true;
    var ver_buff = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, ver_buff);
    gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.STATIC_DRAW);
    this.glBuffers.positions = ver_buff;
    if (this.texcoords) 
    {
      var tex_buff = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, tex_buff);
      gl.bufferData(gl.ARRAY_BUFFER, this.texcoords, gl.STATIC_DRAW);
      this.glBuffers.texcoords = tex_buff;
    }
    if (this.normals) 
    {
      var nor_buff = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, nor_buff);
      gl.bufferData(gl.ARRAY_BUFFER, this.normals, gl.STATIC_DRAW);
      this.glBuffers.normals = nor_buff;
    }
    if (this.indices) 
    {
      this.glBuffers.indices = [];
      for (var i = 0; i < this.lodNumber; i++) 
      {
        var ind_buff = gl.createBuffer();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ind_buff);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices[i], gl.STATIC_DRAW);
        this.glBuffers.indices[i] = ind_buff;
      }
    }
  },
  // Create GL buffer object for Vertex-contant batching
  makeVBOForBatch : function(gl) 
  {
    this.isReadyForBatch = true;
    
    var verNum = this.vertices.length/3;
    if (this.indices)
      var indicesNum = this.indices[0].length;
    var max_obj = Math.floor( GEW_MATH_MAX_USHORT/ verNum );
    
    max_obj = Math.min( max_obj, GEW_MAX_OBJ_VCB);
    this.maxObjPerBatch = max_obj;
    
    var ver = new WebGLFloatArray(max_obj*verNum*3);
    var tex = new WebGLFloatArray(max_obj*verNum*2);
    var nor = new WebGLFloatArray(max_obj*verNum*3);
    var objIdx = new WebGLFloatArray(max_obj*verNum);
    if (this.indices)
      var ind = new WebGLUnsignedShortArray(max_obj*indicesNum);
    
    for(var i=0;i<max_obj;i++)
    {
      var verOffset = i*verNum;
      ver.set(this.vertices, verOffset*3);
      if (this.texcoords) 
        tex.set(this.texcoords,verOffset*2);
      if (this.normals) 
        nor.set(this.normals,verOffset*3);
        
      for(var v=0;v<verNum;v++)
        objIdx[verOffset+v] = i;  
        
      if (this.indices)   
      {
        for(var k=0;k<indicesNum;k++)
        {
          ind[i*indicesNum+k] = this.indices[0][k]+ verOffset;
        }
      }     
    }
        
    var ver_buff = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, ver_buff);
    gl.bufferData(gl.ARRAY_BUFFER, ver, gl.STATIC_DRAW);
    this.batchGLBuffers.positions = ver_buff;
    
    var obj_idx_buff = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, obj_idx_buff);
    gl.bufferData(gl.ARRAY_BUFFER, objIdx, gl.STATIC_DRAW);
    this.batchGLBuffers.objIdx = obj_idx_buff;
    
    if (this.texcoords) 
    {
      var tex_buff = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, tex_buff);
      gl.bufferData(gl.ARRAY_BUFFER, tex, gl.STATIC_DRAW);
      this.batchGLBuffers.texcoords = tex_buff;
    }
    if (this.normals) 
    {
      var nor_buff = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, nor_buff);
      gl.bufferData(gl.ARRAY_BUFFER, nor, gl.STATIC_DRAW);
      this.batchGLBuffers.normals = nor_buff;
    }
    if (this.indices) 
    {      
      var ind_buff = gl.createBuffer();
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ind_buff);
      gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, ind, gl.STATIC_DRAW);
      this.batchGLBuffers.indices = ind_buff;
    }
  }
}

/////////////////////////////////////////////////////////////////////////////////////////////
// SKINNED MESH
function gewSkinnedMesh()
{
  this.meshType = GEW_SKINNED_MESH;
  this.name = null;
  this.renderType;
  this.isReady = false;
  this.vertices = null;
  this.texcoords = null;
  this.normals = null;
  this.boneIdx = null;
  this.weight = null;
  this.indices = null;
  this.lodNumber = 0;
  this.animationBoneMats = [];
  this.animationKeys = []; // key number of each animation
  this.durations = []; // durration of each animation
  this.boneNumber = 0;
  this.fps = 0;
  this.mlsPerFrame = 0;
    
  this.glBuffers = {}; 
  this.aabbs = [];
  this.spheres = [];
  this.gl = null;
  this.isCreateVBO = true;
  this.url = null;
}
gewSkinnedMesh.prototype = {
  parseSAM1:function(data)
  {
    if( data[0]!='s'||data[1]!='a'||data[2]!='m'||data[3]!='1')
    {
      alert("gewStaticMesh::parseSAM1:This file isn't a sam1 file");
    }
    this.renderType =  GEW_TRIANGLES_INDICES;
    var offset = 4;
    var type = gewReadUByte(data, offset);
    offset++;
    var ver_fp_format = gewReadUByte(data, offset);offset++;
    var vMin = gewReadInt16(data,offset);offset+=2;
    var vMax = gewReadInt16(data,offset);offset+=2; 
    var tex_fp_format = gewReadUByte(data, offset);offset++;
    var tMin = gewReadInt16(data,offset);offset+=2;
    var tMax = gewReadInt16(data,offset);offset+=2; 
    var nor_fp_format = gewReadUByte(data, offset);offset++;
    var nMin = gewReadInt16(data,offset);offset+=2;
    var nMax = gewReadInt16(data,offset);offset+=2;     
    var weight_fp_format = gewReadUByte(data, offset);offset++;
    var wMin = gewReadInt16(data,offset);offset+=2;
    var wMax = gewReadInt16(data,offset);offset+=2;
    var mat_fp_format = gewReadUByte(data, offset);offset++;
    var mMin = gewReadInt16(data,offset);offset+=2;
    var mMax = gewReadInt16(data,offset);offset+=2;
    
    var vertex_count = gewReadUInt32(data, offset);
    offset += 4;
    var animation_count = gewReadUInt16(data,offset);offset+=2;
    var animation_mat_count = gewReadUInt16(data,offset);offset+=2;
    this.fps = gewReadUInt16(data,offset);
    this.mlsPerFrame = 1000./this.fps;    
    offset+=2;
    var bone_count = gewReadUInt16(data,offset);
    offset+=2;
    this.boneNumber = bone_count;
    var lod_count = gewReadUByte(data, offset);
    offset++;
    this.lodNumber = lod_count;
    var indies_count = [];
    this.indices = [];
    for(var i=0;i<lod_count;i++)
    {
      indies_count[i] = gewReadUInt32(data, offset);
      offset += 4;
      this.indices[i] = new WebGLUnsignedShortArray(indies_count[i]);
    }
    offset = 128;
    var vertices = new WebGLFloatArray(vertex_count*3);
    var texcoords = new WebGLFloatArray(vertex_count * 2);
    var normals = new WebGLFloatArray(vertex_count * 3);
    var boneIdx = new WebGLFloatArray(vertex_count*4);
    var weight = new WebGLFloatArray(vertex_count*4);
    this.vertices = vertices;
    this.texcoords = texcoords;
    this.normals = normals;
    this.boneIdx = boneIdx;
    this.weight = weight;
    var byte_per_ver_coord = gewFixedPointSize(ver_fp_format);
    var byte_per_tex_coord = gewFixedPointSize(tex_fp_format);
    var byte_per_nor_coord = gewFixedPointSize(nor_fp_format);
    var byte_per_weight = gewFixedPointSize(weight_fp_format);
    for (i = 0; i < vertex_count; i++) 
    {
      vertices[i * 3] = gewReadFixed(data, offset,ver_fp_format,vMin,vMax);
      offset += byte_per_ver_coord;
      vertices[i * 3 + 1] = gewReadFixed(data, offset,ver_fp_format,vMin,vMax);
      offset += byte_per_ver_coord;
      vertices[i * 3 + 2] = gewReadFixed(data, offset,ver_fp_format,vMin,vMax);
      offset += byte_per_ver_coord;
      texcoords[i * 2] = gewReadFixed(data, offset,tex_fp_format,tMin,tMax);
      offset += byte_per_tex_coord;
      texcoords[i * 2 + 1] = gewReadFixed(data, offset,tex_fp_format,tMin,tMax);
      offset += byte_per_tex_coord;
      normals[i * 3] = gewReadFixed(data, offset,nor_fp_format,nMin,nMax);
      offset += byte_per_nor_coord;
      normals[i * 3 + 1] = gewReadFixed(data, offset,nor_fp_format,nMin,nMax);
      offset += byte_per_nor_coord;
      normals[i * 3 + 2] = gewReadFixed(data, offset,nor_fp_format,nMin,nMax);
      offset += byte_per_nor_coord;
      boneIdx[i*4] = gewReadUByte(data,offset);offset++;
      boneIdx[i*4+1] = gewReadUByte(data,offset);offset++;
      boneIdx[i*4+2] = gewReadUByte(data,offset);offset++;
      boneIdx[i*4+3] = gewReadUByte(data,offset);offset++;
      weight[i*4] = gewReadFixed(data, offset,weight_fp_format,wMin,wMax);
      offset+= byte_per_weight;
      weight[i*4+1] = gewReadFixed(data, offset,weight_fp_format,wMin,wMax);
      offset+= byte_per_weight;
      weight[i*4+2] = gewReadFixed(data, offset,weight_fp_format,wMin,wMax);
      offset+= byte_per_weight;
      weight[i*4+3] = gewReadFixed(data, offset,weight_fp_format,wMin,wMax);
      offset+= byte_per_weight;
    }
    for (i = 0; i < lod_count; i++) 
    {
      var indices = this.indices[i];
      for (var j = 0; j < indies_count[i]; j++) 
      {
        indices[j] = gewReadUInt16(data, offset);
        offset += 2;
      }
    }
    
    var animationMats = [];
    // Read deform mat
    for(var m=0;m<animation_mat_count;m++)
    {
      var boneMat = [];
      for(var k =0;k<16;k++)
      {
        boneMat[k] = gewReadFixed(data, offset,mat_fp_format,mMin,mMax);
        offset += 2;
      } 
      animationMats.push(boneMat);
    }    
    var animationMatIndices = [];
    // Read animation matrix indices per frame per bone
    for(var a=0;a<animation_count;a++)
    {
      var indices_num = gewReadUInt16(data, offset);
      offset += 2;
      var key_count = indices_num/bone_count;
      this.animationKeys.push(key_count);
      //console.log(key_count);
      this.durations.push( (key_count-1)*this.mlsPerFrame);
      animationMatIndices[a] = [];      
      for(var i=0;i<indices_num;i++){
        animationMatIndices[a][i] = gewReadUInt16(data, offset);offset += 2;        
      }      
    }
    
    var aniBoneMats = this.animationBoneMats;
    for(var a=0;a<animation_count;a++)
    {
      aniBoneMats[a] = [];
      var key_count = this.animationKeys[a];
      for(var k=0;k<key_count;k++)
      {
        var boneMatBuf = new WebGLFloatArray(bone_count*16);
        for(var b=0;b<bone_count;b++)
        {
          var idx = animationMatIndices[a].shift();
          var bone_mat = animationMats[idx];
          for(var i=0;i<16;i++)
          {
            boneMatBuf[b*16+i] = bone_mat[i];              
          }
        }
        aniBoneMats[a].push(boneMatBuf);
      }
    }
    
    // Read bounding volume
    for(var a=0;a<animation_count;a++)
    {      
      var aabb = new gewAabb();
      var sphere = new gewSphere(); 
      var aabbMin = new gewVec3();
      var aabbMax = new gewVec3();    
      aabbMin.x = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
      offset += 4;
      aabbMin.y = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
      offset += 4;
      aabbMin.z = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
      offset += 4;
      aabbMax.x = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
      offset += 4;
      aabbMax.y = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
      offset += 4;
      aabbMax.z = gewReadFixed(data, offset, GEW_FIXED_POINT_32);        
      offset += 4;
      sphere.center.x = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
      offset += 4;
      sphere.center.y = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
      offset += 4;
      sphere.center.z = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
      offset += 4;
      sphere.radius = gewReadFixed(data, offset, GEW_FIXED_POINT_32);
      offset += 4;  
      aabb.init(aabbMin,aabbMax);
      this.aabbs.push(aabb);
      this.spheres.push(sphere);    
    }
    if(this.isCreateVBO!=false)
      this.makeVBO(this.gl)
    this.isReady = true;
  },
  // Create GL buffer object that contain mesh data
  makeVBO : function(gl) 
  {
    this.isCreateVBO = true;
    var ver_buff = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, ver_buff);
    gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.STATIC_DRAW);
    this.glBuffers.positions = ver_buff;
    if (this.texcoords) 
    {
      var tex_buff = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, tex_buff);
      gl.bufferData(gl.ARRAY_BUFFER, this.texcoords, gl.STATIC_DRAW);
      this.glBuffers.texcoords = tex_buff;
    }
    if (this.normals) 
    {
      var nor_buff = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, nor_buff);
      gl.bufferData(gl.ARRAY_BUFFER, this.normals, gl.STATIC_DRAW);
      this.glBuffers.normals = nor_buff;
    }
    if (this.indices) 
    {
      this.glBuffers.indices = [];
      for (var i = 0; i < this.lodNumber; i++) 
      {
        var ind_buff = gl.createBuffer();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ind_buff);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices[i], gl.STATIC_DRAW);
        this.glBuffers.indices[i] = ind_buff;
      }
    }
    if( this.weight)
    {
      var weight_buff = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, weight_buff);
      gl.bufferData(gl.ARRAY_BUFFER, this.weight, gl.STATIC_DRAW);
      this.glBuffers.weight = weight_buff;
    }
    if( this.boneIdx)
    {
      var bone_idx_buff = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, bone_idx_buff);
      gl.bufferData(gl.ARRAY_BUFFER, this.boneIdx, gl.STATIC_DRAW);
      this.glBuffers.boneIdx = bone_idx_buff;
    }
  }
}

/////////////////////////////////////////////////////////////////////////////////////////////
// KEYFRAME MESH
function gewKeyframeAnimation()
{
  this.keyframes = [];
  this.timeline = [];
  this.inter_function = [];
  this.properties = new WebGLFloatArray(16);// Key_num|Pos_Key_num|Tex_key_num|Nor_key_num|Current_time|Timeline(6)|inter_func(5)
}
function gewKeyframeMesh()
{
  this.meshType = GEW_KEYFRAME_MESH;
  this.renderType; // specify how to vertices data is stored in this mesh
  this.name = null;
  this.isReady = false;
  this.verticesArray = [];
  this.texcoordsArray = [];
  this.normalsArray = [];
  this.indices = null;
  this.lodNumber = 0;
  this.keyframeNumber = 0;
  this.isStaticPosition;
  this.isStaticTexcoord;
  this.isStaticNormal;
  this.animations = {};
  this.glBuffers = {}; 
  this.aabb = new gewAabb();
  this.boundingSphere = new gewSphere();
  this.gl = null;
  this.isCreateVBO = true;
  this.url = null;
}
gewKeyframeAnimation.prototype =
{
  prepareProperties: function(isStaticPos, isStaticTex, isStaticNor)
  {
    var pro = this.properties;
    pro[0] = this.keyframes.length;
    if (pro[0] > 6 || (pro[0] == 6 && this.keyframes[0] != this.keyframes[5])) 
    {
      alert("gewKeyframeAnimation:Error:This animation is too long ( have " + pro[0] + " key)");
    }
    if (pro[0] > 5) 
      pro[0] = 5;
    if (!isStaticPos) 
      pro[1] = pro[0];
    else 
      pro[1] = 1;
    if (!isStaticTex) 
      pro[2] = pro[0];
    else 
      pro[2] = 1;
    if (!isStaticNor) 
      pro[3] = pro[0];
    else 
      pro[3] = 1;
    pro[4] = 0;
    for (var i = 0; i < 6; i++) 
    {
      if (i < this.timeline.length) 
        pro[5 + i] = this.timeline[i];
      else 
        pro[5 + i] = 0;
    }
    for (var i = 0; i < 5; i++) 
    {
      if (i < this.inter_function.length) 
        pro[11 + i] = this.inter_function[i];
      else 
        pro[11 + i] = 0;
    }
  }
}
gewKeyframeMesh.prototype =
{
  parseKAM1:function(data)
  {
    if( data[0]!='k'||data[1]!='a'||data[2]!='m'||data[3]!='1')
    {
      alert("gewStaticMesh::parseKAM1:This file isn't a kam1 file");
    }
    this.renderType =  GEW_TRIANGLES_INDICES;
    var offset = 4;
    var type = gewReadUByte(data, offset);
    offset++;
    var ver_fp_format = gewReadUInt32(data, offset);
    offset += 4;
    var tex_fp_format = gewReadUInt32(data, offset);
    offset += 4;
    var nor_fp_format = gewReadUInt32(data, offset);
    offset += 4;
    var ver_sign_bit = gewFixedPointSignBit(ver_fp_format);
    var tex_sign_bit = gewFixedPointSignBit(tex_fp_format);
    var nor_sign_bit = gewFixedPointSignBit(nor_fp_format);
    var ver_int_bit = gewFixedPointIntegerBit(ver_fp_format);
    var tex_int_bit = gewFixedPointIntegerBit(tex_fp_format);
    var nor_int_bit = gewFixedPointIntegerBit(nor_fp_format);
    var ver_frac_bit = gewFixedPointFractionalBit(ver_fp_format);
    var tex_frac_bit = gewFixedPointFractionalBit(tex_fp_format);
    var nor_frac_bit = gewFixedPointFractionalBit(nor_fp_format);
    var byte_per_ver_coord = gewFixedPointSize(ver_fp_format);
    var byte_per_tex_coord = gewFixedPointSize(tex_fp_format);
    var byte_per_nor_coord = gewFixedPointSize(nor_fp_format);
    var vertex_count = gewReadUInt32(data, offset);
    offset += 4;
    var animation_count = gewReadUInt32(data, offset);
    offset += 4;
    var keyframe_count = gewReadUInt32(data, offset);
    offset += 4;
    this.keyframeNumber = keyframe_count;
    var options = gewReadUInt32(data, offset);
    offset += 4;
    this.isStaticPosition = ((options & 0x04) > 0) ? true : false;
    this.isStaticTexcoord = ((options & 0x02) > 0) ? true : false;
    this.isStaticNormal = ((options & 0x01) > 0) ? true : false;
    var lod_count = gewReadUByte(data, offset);
    offset++;
    this.lodNumber = lod_count;
    var indies_count = [];
    this.indices = [];
    for (var i = 0; i < lod_count; i++) 
    {
      indies_count[i] = gewReadUInt32(data, offset);
      offset += 4;
      this.indices[i] = new WebGLUnsignedShortArray(indies_count[i]);
    }
    offset = 96;
    var verticesArray = this.verticesArray;
    var normalsArray = this.normalsArray;
    var texcoordsArray = this.texcoordsArray;
    if (this.isStaticPosition) 
      verticesArray[0] = new WebGLFloatArray(vertex_count * 3);
    if (this.isStaticNormal) 
      normalsArray[0] = new WebGLFloatArray(vertex_count * 3);
    if (this.isStaticTexcoord) 
      texcoordsArray[0] = new WebGLFloatArray(vertex_count * 2);
    for (var i = 0; i < keyframe_count; i++) 
    {
      if (!this.isStaticPosition) 
        verticesArray[i] = new WebGLFloatArray(vertex_count * 3);
      if (!this.isStaticNormal) 
        normalsArray[i] = new WebGLFloatArray(vertex_count * 3);
      if (!this.isStaticTexcoord) 
        texcoordsArray[i] = new WebGLFloatArray(vertex_count * 2);
    }
    // read position 
    var position_key_num = keyframe_count;
    if (this.isStaticPosition)
    {
      position_key_num = 1;
    }
    for (var i = 0; i < vertex_count; i++)
    {
      for (var key = 0; key < position_key_num; key++) 
      {
        verticesArray[key][i * 3] = gewReadFixed(data, offset, ver_sign_bit, ver_int_bit, ver_frac_bit);
        offset += byte_per_ver_coord;
        verticesArray[key][i * 3 + 1] = gewReadFixed(data, offset, ver_sign_bit, ver_int_bit, ver_frac_bit);
        offset += byte_per_ver_coord;
        verticesArray[key][i * 3 + 2] = gewReadFixed(data, offset, ver_sign_bit, ver_int_bit, ver_frac_bit);
        offset += byte_per_ver_coord;
      }
    }
    // read texcoord
    var texcoords_key_num = keyframe_count;
    if (this.isStaticTexcoord) 
      texcoords_key_num = 1;
    for (var i = 0; i < vertex_count; i++)
    {
      for (var key = 0; key < texcoords_key_num; key++) 
      {
        texcoordsArray[key][i * 2] = gewReadFixed(data, offset, tex_sign_bit, tex_int_bit, tex_frac_bit);
        offset += byte_per_tex_coord;
        texcoordsArray[key][i * 2 + 1] = gewReadFixed(data, offset, tex_sign_bit, tex_int_bit, tex_frac_bit);
        offset += byte_per_tex_coord;
      }
    }
    // read normal
    var normals_key_num = keyframe_count;
    if (this.isStaticNormal) 
      normals_key_num = 1;
    for (var i = 0; i < vertex_count; i++)
    { 
      for (var key = 0; key < normals_key_num; key++) 
      {
        normalsArray[key][i * 3] = gewReadFixed(data, offset, nor_sign_bit, nor_int_bit, nor_frac_bit);
        offset += byte_per_nor_coord;
        normalsArray[key][i * 3 + 1] = gewReadFixed(data, offset, nor_sign_bit, nor_int_bit, nor_frac_bit);
        offset += byte_per_nor_coord;
        normalsArray[key][i * 3 + 2] = gewReadFixed(data, offset, nor_sign_bit, nor_int_bit, nor_frac_bit);
        offset += byte_per_nor_coord;
      }
    }
    // Read lod infos
    for (i = 0; i < lod_count; i++) 
    {
      var indices = this.indices[i];
      for (var j = 0; j < indies_count[i]; j++) 
      {
        indices[j] = gewReadUInt16(data, offset);
        offset += 2;
      }
    }
    // Read animation infos
    for(i=0;i<animation_count;i++)
    {
      var key_num = gewReadUInt16(data, offset);
      offset += 2;
      var str = data.substring(offset, offset + 20);
      offset += 20;
      var name = str.split(' ', 2);
      var ani = new gewKeyframeAnimation();
      this.animations[name[0]] = ani;
      for (var k = 0; k < key_num; k++) 
      {
        ani.keyframes[k] = gewReadUInt16(data, offset);
        offset += 2;
      }
      for(var k=0;k<key_num;k++)
      {
        ani.timeline[k] = gewReadUInt16(data,offset)/24.;offset+=2;
      }
      for (var k = 0; k < (key_num - 1); k++) 
      {
        ani.inter_function[k] = gewReadUInt16(data, offset);
        offset += 2;
      }
      ani.prepareProperties(this.isStaticPosition,this.isStaticTexcoord,this.isStaticNormal);	  
    }
    // Read bounding volume
    var aabb = this.aabb;
    var aabbMin = new gewVec3();
    var aabbMax = new gewVec3();
    var sphere = this.boundingSphere;
    aabbMin.x = gewReadFixed(data, offset, ver_sign_bit, ver_int_bit, ver_frac_bit);
    offset += byte_per_ver_coord;
    aabbMin.y = gewReadFixed(data, offset, ver_sign_bit, ver_int_bit, ver_frac_bit);
    offset += byte_per_ver_coord;
    aabbMin.z = gewReadFixed(data, offset, ver_sign_bit, ver_int_bit, ver_frac_bit);
    offset += byte_per_ver_coord;
    aabbMax.x = gewReadFixed(data, offset, ver_sign_bit, ver_int_bit, ver_frac_bit);
    offset += byte_per_ver_coord;
    aabbMax.y = gewReadFixed(data, offset, ver_sign_bit, ver_int_bit, ver_frac_bit);
    offset += byte_per_ver_coord;
    aabbMax.z = gewReadFixed(data, offset, ver_sign_bit, ver_int_bit, ver_frac_bit);
    offset += byte_per_ver_coord;
    aabb.init(aabbMin,aabbMax);
    
    sphere.center.x = gewReadFixed(data, offset, ver_sign_bit, ver_int_bit, ver_frac_bit);
    offset += byte_per_ver_coord;
    sphere.center.y = gewReadFixed(data, offset, ver_sign_bit, ver_int_bit, ver_frac_bit);
    offset += byte_per_ver_coord;
    sphere.center.z = gewReadFixed(data, offset, ver_sign_bit, ver_int_bit, ver_frac_bit);
    offset += byte_per_ver_coord;
    sphere.radius = gewReadFixed(data, offset, ver_sign_bit, ver_int_bit, ver_frac_bit);
    offset += byte_per_ver_coord;
    
    if(this.isCreateVBO!=false)
      this.makeVBO(this.gl)  
    this.isReady = true;
  },
  
  // Create GL buffer object that contain mesh data
  makeVBO : function(gl) 
  {
    this.isCreateVBO = true;
    this.glBuffers.positionsArray = [];
    this.glBuffers.texcoordsArray = [];
    this.glBuffers.normalsArray = [];
    this.glBuffers.indices = [];
    for (var i = 0; i < this.verticesArray.length; i++) 
    {
      var ver_buff = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, ver_buff);
      gl.bufferData(gl.ARRAY_BUFFER, this.verticesArray[i], gl.STATIC_DRAW);
      this.glBuffers.positionsArray[i] = ver_buff;
    }
    for (var i = 0; i < this.texcoordsArray.length; i++) 
    {
      var ver_buff = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, ver_buff);
      gl.bufferData(gl.ARRAY_BUFFER, this.texcoordsArray[i], gl.STATIC_DRAW);
      this.glBuffers.texcoordsArray[i] = ver_buff;
    }
    for (var i = 0; i < this.normalsArray.length; i++) 
    {
      var ver_buff = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, ver_buff);
      gl.bufferData(gl.ARRAY_BUFFER, this.normalsArray[i], gl.STATIC_DRAW);
      this.glBuffers.normalsArray[i] = ver_buff;
    }
    if (this.indices) 
    {
      for (var i = 0; i < this.lodNumber; i++) 
      {
        var ind_buff = gl.createBuffer();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ind_buff);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices[i], gl.STATIC_DRAW);
        this.glBuffers.indices[i] = ind_buff;
      }
    }
  }
}